﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Etapa1.SteeringExperiments;
using PloobsEngine.SceneControl;
using PloobsEngine.Physics;
using PloobsEngine;
using PloobsEngine.Cameras;
using PloobsEngine.Input;
using PloobsEngineDemo3;
using PloobsEngine.Draw;
using PloobsEngine.Physics.Bepu;
using PloobsEngine.Modelo;
using PloobsEngine.Material;
using Microsoft.Xna.Framework;
using PloobsEngine.Entity;
using PloobsEngine.Features;
using PloobsEngine.Commands;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine.Light;

namespace Etapa1.Screens
{
    public class SteeringBehaviorScreen : SceneScreen
    {
        private BepuPhysicWorld physicWorld;
        private BrutalForceWorld mundo;
        private EngineStuff engine;        
        ICamera cam;
        SteeringAgent ag;
        PathDescription pd;

        List<Cohesion> co = new List<Cohesion>();
        List<Separation> se = new List<Separation>();
        List<Alignment> al = new List<Alignment>();
        List<PursuitOffset> pu = new List<PursuitOffset>();

        public SteeringBehaviorScreen(EngineStuff es)
        {
            this.engine = es;
            physicWorld = new BepuPhysicWorld();            
            mundo = new BrutalForceWorld(physicWorld);                        
            engine.Game.IsFixedTimeStep = true;
        }
        
        public override void LoadContent(EngineStuff es)
        {
            InputAdvanced inp = new InputAdvanced(engine);
            engine.AddComponent(inp);

            base.LoadContent(es);            

            #region Models

            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\cenario");
                sm.LoadModelo();
                IPhysicObject pi = new TriangleMeshObject(Vector3.Zero, sm);
                CustomDeferred shader = new CustomDeferred(false, false, false, false);
                shader.SpecularIntensity = 0;
                shader.SpecularPower = 0;
                shader.Initialize();
                IMaterial mat = new Material(shader, MaterialType.DEFERRED);
                IObject obj3 = new SimpleObject(mat, pi, sm);
                mundo.AddObject(obj3);
            }

            IObject obj4;
            {

                SimpleModel sm = new SimpleModel("..\\Content\\Model\\block", "..\\Content\\Textures\\caixaTex");
                sm.LoadModelo();
                BoxObject pi = new BoxObject(new Vector3(0, 2, 100), 1, 50, new Vector3(3));
                ObjectMover om = new ObjectMover(pi);
                IShader shader = new NormalDeferred();
                shader.Initialize();
                IMaterial mat = new Material(shader, MaterialType.DEFERRED);
                obj4 = new SimpleObject(mat, om, sm);
                obj4.Name = "CONTROLLED";
                ag = new SteeringAgent(obj4);
                ag.SteeringEntityInfo.MaxForce = 20;
                ag.SteeringEntityInfo.MaxVelocity = 20;
                ag.SteeringBehavior.AddBehavior(new Arrive(new Vector3(50, 1, 250), Deceleration.fast, 30), 2, 1);
                //ag.SteeringBehavior.AddBehavior(new Seek(new Vector3(50,0,50)));
                //ag.SteeringBehavior.AddBehavior(new Separation(mundo));
                mundo.AddObject(obj4);
            }




            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\block", "..\\Content\\Textures\\white");
                sm.LoadModelo();
                BoxObject pi = new BoxObject(new Vector3(100, 2, 100), 1, 50, new Vector3(3));
                ObjectMover om = new ObjectMover(pi);                
                IShader shader = new NormalDeferred();
                shader.Initialize();
                IMaterial mat = new Material(shader, MaterialType.DEFERRED);
                IObject obj5 = new SimpleObject(mat, om, sm);
                obj5.Name = "WHITE";
                SteeringAgent ag = new SteeringAgent(obj5);
                ag.SteeringEntityInfo.MaxForce = 70;
                ag.SteeringEntityInfo.MaxVelocity = 50;
                pd = new PathDescription();
                pd.Closed = true;
                pd.Points = new List<Vector3>() { new Vector3(300, 0, 0), new Vector3(220, 0, 0), new Vector3(150, 0, 50), new Vector3(150, 0, 150), new Vector3(300, 0, 300) };
                ag.SteeringBehavior.AddBehavior(new FollowPath(pd, 10));
                //ag.SteeringBehavior.AddBehavior(new Seek(new Vector3(600,0,0)));
                mundo.AddObject(obj5);

            }
            
            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    

                    SimpleModel sm = new SimpleModel("..\\Content\\Model\\block");
                    sm.LoadModelo();
                    BoxObject pi = new BoxObject(new Vector3(100 + i * 20, 1, 100 + j * 20), 1, 5, new Vector3(1));
                    pi.LimitRotationAxis(false, true, false);
                    ObjectMover om = new ObjectMover(pi);
                    IShader shader = new NormalDeferred();
                    shader.Initialize();
                    IMaterial mat = new Material(shader, MaterialType.DEFERRED);
                    IObject obj5 = new SimpleObject(mat, om, sm);
                    obj5.Name = "BLACK-" + i + "-" + j;
                    SteeringAgent ag = new SteeringAgent(obj5);
                    ag.SteeringEntityInfo.MaxForce = 50;
                    ag.SteeringEntityInfo.MaxVelocity = 30;

                    //ag.SteeringBehavior.AddBehavior(new Wander(2000, 100, 20, 10, Deceleration.fast), 50, 0.5f);
                    PursuitOffset po = new PursuitOffset(obj4, new Vector2(-i * 20 - 40, 0));
                    pu.Add(po);
                    ag.SteeringBehavior.AddBehavior(po, 4, 1);

                    float ray = 100;

                    Separation sep = new Separation(mundo);
                    sep.RayOfAction = ray;
                    se.Add(sep);
                    ag.SteeringBehavior.AddBehavior(sep, 1, 5);

                    Alignment ali = new Alignment(mundo);
                    ali.RayOfAction = ray;
                    al.Add(ali);
                    ag.SteeringBehavior.AddBehavior(ali, 1, 5);

                    Cohesion coh = new Cohesion(mundo);
                    coh.RayOfAction = ray;
                    co.Add(coh);
                    ag.SteeringBehavior.AddBehavior(coh, 1, 1);
                    mundo.AddObject(obj5);
                    EntityMapper.getInstance().AddgrouptagRecieveEntity("steer", obj5);

                    ali.SteeringState = SteeringState.Innactive;
                    coh.SteeringState = SteeringState.Innactive;
                    po.SteeringState = SteeringState.Innactive;
                    sep.SteeringState = SteeringState.Innactive;
                }
                
            }

            
            
            //{
            //    SimpleModel sm = new SimpleModel("..\\Content\\Model\\block", "..\\Content\\Textures\\white");
            //    sm.LoadModelo();
            //    IPhysicObject pi = new BoxObject(new Vector3(100, 5, 100), 1, 50, new Vector3(5));
            //    IShader shader = new NormalDeferred();
            //    shader.Initialize(engine);
            //    IMaterial mat = new Material(shader, MaterialType.DEFERRED);
            //    IObject obj5 = new SimpleObject(mat, pi, sm);
            //    SteeringAgent ag = new SteeringAgent(obj5);
            //    ag.SteeringEntityInfo.MaxForce = 100;
            //    ag.SteeringEntityInfo.MaxVelocity = 40;
            //    ag.SteeringEntityInfo.MaxAngularVelocity = 100;
            //    ag.SteeringBehavior.AddBehavior(new Wander(2000, 30, 20, 10, Deceleration.normal), 1, 1);
            //    //ag.SteeringBehavior.AddBehavior(new PursuitOffset(obj4, new Vector2(-10, 15)));
            //    //ag.SteeringBehavior.AddBehavior(new Alignment(mundo),0,1);
            //    //ag.SteeringBehavior.AddBehavior(new Evade(obj4,30), 0, 0.5f);
            //    mundo.AddObject(obj5);
            //}

            //{
            //    SimpleModel sm = new SimpleModel("..\\Content\\Model\\block", "..\\Content\\Textures\\white");
            //    sm.LoadModelo();
            //    IPhysicObject pi = new BoxObject(new Vector3(100, 3, 100), 1, 50, new Vector3(5));
            //    pi.isMotionLess = true;
            //    IShader shader = new NormalDeferred();
            //    shader.Initialize(engine);
            //    IMaterial mat = new Material(shader, MaterialType.DEFERRED);
            //    IObject obj5 = new SimpleObject(mat, pi, sm);
            //    SteeringAgent ag = new SteeringAgent(obj5);
            //    ag.SteeringEntityInfo.MaxForce = 200;
            //    ag.SteeringEntityInfo.MaxVelocity = 10;
            //    ag.SteeringBehavior.AddBehavior(new Wander(3000, 10, 100, 10, Deceleration.normal), 1, 1);
            //    //ag.SteeringBehavior.AddBehavior(new PursuitOffset(obj4, new Vector2(-10, -15)));
            //    //ag.SteeringBehavior.AddBehavior(new Evade(obj4,30), 0, 0.5f);
            //    mundo.AddObject(obj5);
            //}
            
            #endregion

            cam = new CameraFirstPerson(true);
            //cam = new StaticCamera(new Vector3(100,50,100), Vector3.Zero);
            cam.FarPlane = 3000;
            
            LightThrowBepu lt = new LightThrowBepu(mundo, engine);

            #region NormalLight
            DirectionalLight ld1 = new DirectionalLight(Vector3.Left, Color.White);
            DirectionalLight ld2 = new DirectionalLight(Vector3.Right, Color.White);
            DirectionalLight ld3 = new DirectionalLight(Vector3.Backward, Color.White);
            DirectionalLight ld4 = new DirectionalLight(Vector3.Forward, Color.White);
            DirectionalLight ld5 = new DirectionalLight(Vector3.Down, Color.White);
            float li = 0.4f;
            ld1.LightIntensity = li;
            ld2.LightIntensity = li;
            ld3.LightIntensity = li;
            ld4.LightIntensity = li;
            ld5.LightIntensity = li;
            mundo.AddLight(ld1);
            mundo.AddLight(ld2);
            mundo.AddLight(ld3);
            mundo.AddLight(ld4);
            mundo.AddLight(ld5);
            #endregion

            ////Criacao de uma Tecnica
            DeferredRenderTechnic dr = new DeferredRenderTechnic(engine);

            IRenderTechnic[] rt = new IRenderTechnic[] { dr };
            this.RenderTechnics = rt;
            mundo.AddCamera(cam);
            this.World = mundo;

            Picking pk = new Picking();
            engine.AddComponent(pk);

            PickingParameters pp = new PickingParameters(cam, mundo, MouseButtons.RightButton);
            pp.Method = SegmentInterceptMethod.ALL;
            PickingRedirectorCallbackObserver prc = new PickingRedirectorCallbackObserver();
            prc.redirectPicking += new RedirectPicking(prc_redirectPicking);
            pp.Observers.Add(prc);
            CommandProcessor.getCommandProcessor().SendCommandSyncronous(pp);

            {
                InputPlayableKeyBoard ipp = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.P, KeyPressed, EntityType.TOOLS);
                BindKeyCommand bkk = new BindKeyCommand(ipp, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bkk);

                ipp = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.O, KeyPressed, EntityType.TOOLS);
                bkk = new BindKeyCommand(ipp, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bkk);

                ipp = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.I, KeyPressed, EntityType.TOOLS);
                bkk = new BindKeyCommand(ipp, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bkk);

                ipp = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.U, KeyPressed, EntityType.TOOLS);
                bkk = new BindKeyCommand(ipp, BindAction.ADD);
                CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bkk);
            }


        }

        public void KeyPressed(InputPlayableKeyBoard ipk)
        {            
            if (ipk.Keys[0] == Keys.P)
            {
                foreach (var item in al)
                {
                    if (item.SteeringState == SteeringState.Active)
                    {
                        item.DisableSteering();
                    }
                    else
                    {
                        item.EnableSteering();
                    }
                }
            }
            if (ipk.Keys[0] == Keys.O)
            {
                foreach (var item in se)
                {
                    if (item.SteeringState == SteeringState.Active)
                    {
                        item.DisableSteering();
                    }
                    else
                    {
                        item.EnableSteering();
                    }
                }
            }
            if (ipk.Keys[0] == Keys.I)
            {
                foreach (var item in co)
                {
                    if (item.SteeringState == SteeringState.Active)
                    {
                        item.DisableSteering();
                    }
                    else
                    {
                        item.EnableSteering();
                    }
                }
            }
            if (ipk.Keys[0] == Keys.U)
            {
                foreach (var item in pu)
                {
                    if (item.SteeringState == SteeringState.Active)
                    {
                        item.DisableSteering();
                    }
                    else
                    {
                        item.EnableSteering();
                    }
                }
            }

        }

        RayTestInfo ri = null;
        void prc_redirectPicking(object obj)
        {
            ri = obj as RayTestInfo;
            ag.SteeringBehavior.GetBehavior<Arrive>(BehaviorType.arrive).Target = ri.ImpactPosition;            
            //ag.SteeringBehavior.GetBehavior<Seek>(BehaviorType.seek).Target = ri.ImpactPosition;            
        }

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            if (ri != null)
            {
                Drawing.Draw3dLine(ri.ImpactPosition, ri.ImpactPosition + Vector3.Up * 20, Color.Red);
                Drawing.DrawCircleOrDisk(5, Vector3.Up, ri.ImpactPosition, Color.Purple, 100, true, true);
            }

            foreach (var item in pd.Points)
            {
                Drawing.Draw3dLine(item, item + Vector3.Up * 20, Color.Gold);
            }            
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
        }


    }
}

